Freeradius 3.2.10 环境搭建以及 PAP 和 CHAP 两种认证方式的测试
Freeradius 简介
是什么?
FreeRADIUS 是目前全球使用最广泛、功能最强大的开源 RADIUS 服务器软件。如果把网络比作一座企业大楼,FreeRADIUS 就是这座大楼的 “智能门禁系统控制器”。它专门用来解决网络世界里的 AAA 问题:
- Authentication(认证):你是谁?(核对你的用户名、密码、证书或动态验证码)
- Authorization(授权):你能干什么?(决定把你分配到哪个 VLAN、给你的网速限制是多少、允许你访问哪些子网)
- Accounting(计费/审计):你干了什么?(记录你什么时候上线的、什么时候下线的、一共用了多少流量、在线多长时间)
能干什么?
在顶端看,FreeRADIUS 的功能可以划分为核心协议支持、认证源集成(数据面)、策略控制(控制面) 以及高级架构能力四个维度:
- 核心 AAA 协议支持:
- RADIUS & RadSec:传统的 UDP 认证/计费,以及基于 TLS 加密的 RADIUS(RadSec,用于跨信任域的安全传输)。
- TACACS+(3.2+ 增强):不仅能做网络准入(RADIUS),还能做网络设备(思科、华为等交换机)的命令行审计和权限分级(Command Authorization)。
- COA / Disconnect-Message:动态授权变更。可以在用户上网途中,强制将其踢下线或动态限速。
- 极其恐怖的 “连接器” 能力(认证源集成):它能对接你公司现有的几乎任何身份系统。
- 标准数据库:MySQL, PostgreSQL, Oracle, Redis。
- 企业身份源:LDAP, Microsoft Active Directory (AD)。
- 现代 Auth:REST API (HTTP JSON 请求)、gRPC、PAM。
- 可编程脚本:支持直接嵌入 Python3、Perl、Lua 脚本来处理复杂的认证逻辑。
- 控制面:EAP 与策略引擎。
- 企业 Wi-Fi 与 802.1X 的基石:完美支持 EAP-PEAP、EAP-TLS(证书认证)、EAP-TTLS。
- Unlang 策略语言:FreeRADIUS 自创的伪代码语言(类似 if/else),可以在不编译源码的情况下,任意修改请求包、判断属性、改写路由逻辑。
核心架构
1 | [ 终端设备 ] (手机/电脑) |
集群架构
大多数商业交换机、无线控制器(AC)或胖 AP 本身就支持配置主/备 RADIUS 服务器(Primary / Secondary Server)。AP 默认把所有认证请求发给 FreeRADIUS 节点 A。如果 节点 A 挂了(心跳超时),AP 会自动将后续请求切到 FreeRADIUS 节点 B。但这种结构只有主备切换,无法做到流量的负载均衡(所有压力都在主节点)。一个标准的生产级 FreeRADIUS 集群通常由三层结构组成:
1 | ┌───────────────────────┐ |
使用支持 UDP 报文分发的负载均衡器(如 LVS / Keepalived + HAProxy,或者硬件负载均衡 F5),将 RADIUS 流量按照轮询或哈希算法分发给后端的多个 FreeRADIUS 节点。难点与细节:
- UDP 无连接特性:RADIUS 基于 UDP,负载均衡器必须开启会话保持(Session Persistence / IP Hash)因为 EAP/PEAP 认证需要经过多次 Request-Response 报文交互,同一个终端的整个 EAP 握手过程必须落到同一台 FreeRADIUS 节点上。
- EAP 状态问题:如果在 PEAP 握手中间报文被甩到了另一台服务器,另一台服务器是没有上文 TLS 内存状态的,会导致认证直接 FAILURE。
FreeRADIUS 集群的关键注意事项:
- 内层 TLS Session Cache(PEAP 重连优化): 如果启用了 PEAP-TLS,并且希望用户在多个节点之间切换时能够快速重连(Fast Resume),各 FreeRADIUS 节点的 TLS 缓存必须共享。通常使用 Memcached 或 Redis 模块来集中存储 TLS Session。
- 后端 Session 状态与计费(Accounting & Radutmp):如果限制 “一个账号只能一人登录”,FreeRADIUS 需要知道当前谁在线。绝不能使用本地文件的 radutmp,必须使用集中式数据库(如 MySQL/PostgreSQL)的 radacct 表或 Redis 来统一记录用户的上线/下线状态。
- 由于各节点配置需要完全一致(包括 clients.conf 里的共享密钥 secret、certs/ 证书文件、mods-enabled/ 模块),生产环境中通常结合 Git + CI/CD,或者 Ansible / Docker Swarm / K8s 来确保配置的一致性更新。
使用场景
- Wi-Fi 认证(802.1X / EAP):你在公司连 Wi-Fi 时,不需要输入统一的傻瓜式密码,而是弹出一个窗口让你输入你自己的员工账号和密码(PEAP-MSCHAPv2)或者加载个人证书(EAP-TLS)。后台处理这个校验过程的,绝大多数就是 FreeRADIUS。
- 运营商与宽带拨号(PPPoE / IPoE):你在家用的电信、联通宽带拨号上网,或者在酒店/机场连接需要手机号登录的 Web 认证网络(Captive Portal),后台通常是由 FreeRADIUS 配合数据库来计费和控制断网。
- VPN 与远程接入:公司员工通过 OpenVPN、WireGuard 或 IPsec VPN 连入内网时,FreeRADIUS 负责对接公司的 LDAP/AD 域控进行二次身份验证。
- 网络设备管理(TACACS+ / RADIUS):网络工程师登录交换机、路由器、防火墙进行配置时,集中管理哪些管理员有权限登录、做了哪些命令操作。
为什么它是行业标准
- 极其庞大的生态与模块化:FreeRADIUS 就像积木一样。它的核心非常轻量,但拥有极其丰富的模块(mods-enabled/)。它可以轻松对接各种后端“账号库”:
- 数据库:MySQL、PostgreSQL、Oracle、MongoDB…
- 目录服务:微软 Active Directory (AD)、OpenLDAP、FreeIPA…
- 现代认证:REST API、OAuth2、双因素认证(MFA/TOTP)…
- 高性能与高并发:它采用 C 语言编写,性能强悍,能够轻松应对每秒成千上万次的并发认证请求,也是很多大型电信运营商的 preferred 选择。
- 强大的策略引擎(Unlang):FreeRADIUS 拥有自己的伪代码脚本语言(Unlang),允许管理员在认证流程中写 if / else 逻辑。例如:“如果这个用户是财务部的,并且是在下班后登录,就把他分配到访客 VLAN 并限制网速为 10Mbps”。
- 100% 开源且免费:相比思科的 ISE、Aruba 的 ClearPass 等动辄几十万上百万的商业 AAA 服务器,FreeRADIUS 是免费的,同时社区非常活跃。
搭建基本的调试环境
下面我们将基于 Docker 容器化技术,搭建一个包含 FreeRADIUS 服务端与独立测试客户端的双容器调试环境。在动手之前,请在你握紧这三把排查问题的 “手术刀”,它们将贯穿你后续的整个调优过程:
freeradius -X:看懂它的 Debug 日志,你就看懂了 RADIUS 协议的本质。
radtest 与 eapol_test:前者用于测试基础的 PAP/CHAP 认证及数据库(SQL/LDAP)连通性;后者则是专攻无线 WPA-Enterprise / EAP 协议的终极利器。
- 模块化思维:FreeRADIUS 的核心配置全部在 mods-enabled/ 目录中。每次只启用和测试一个模块(例如:先测通 SQL,再集成 LDAP),切忌同时修改多个地方,否则会陷入多重错误的泥潭。
服务端环境初始化与挂载
第一步:创建工作目录与提取默认配置。在宿主机执行以下命令,快速拉取镜像并提取出干净的配置:
1 | # 1. 创建测试工作目录 |
第二步:编写 docker-compose.yml。在 fr-test 目录下创建 docker-compose.yml。这里我们采用双容器架构:一个作为 RADIUS 服务端(默认开启 -X 前台调试),另一个作为纯净的客户端(保持后台运行,专门用来跑测试工具)。
1 | version: '3.8' |
第三步:一键启动双容器环境
1 | # 确保清理可能残留的旧同名容器 |
配置独立测试客户端
现在我们要进入 radius-client 容器内部,将其武装成一个功能齐全的测试机。
第一步:进入客户端容器并换源
1 | # 进入客户端容器 |
第二步:安装基础依赖与基础测试工具。
1 | # 我们首先安装编译所需的基础包,以及自带 radtest 工具的官方工具包: |
第三步:源码编译无线终极测试利器 eapol_test。eapol_test 是 wpa_supplicant 源码自带的一个隐藏测试程序,能够模拟真实的无线 AP 和客户端向 RADIUS 发起 PEAP/TLS/TTLS 等复杂的 EAP 认证。
1 | apt-get update && apt-get install -y net-tools |
双向联调与验证
两端就绪后,我们立刻进行联调。通过观测服务端实时日志,验证整个环境的可用性。
第一步:打开服务端 “手术监视器”。在宿主机上新开一个终端,执行以下命令,实时挂起 FreeRADIUS 容器的 -X 调试日志:
1 | docker logs -f radius-server |
第二步:使用 radtest 进行基础认证测试。回到 radius-client 容器内部的命令行中,使用默认的官方测试账号发起一次常规的 Access-Request:
1 | # 添加测试用户 |
解释什么是 NAS
在 RADIUS 架构中,NAS 指的是“网络接入服务器”(Network Access Server)。简单来说,NAS 就是夹在“用户终端”和“FreeRADIUS 服务器”中间的那个“门卫/网关设备”。常见的 NAS 设备有:
- 企业级 Wi-Fi AP / 无线控制器 (AC):你的手机连 Wi-Fi,AP 就是 NAS。
- 网络交换机 (Switch):电脑插网线到交换机端口,交换机就是 NAS。
- VPN 网关 / 路由器:你拨号连 VPN,VPN 服务器就是 NAS。
- 运营商的 PPPoE 宽带接入设备 (BNG/BRAS):你在家宽带拨号,运营商的接入网关就是 NAS。
NAS 在认证流程中扮演的角色:
- 翻译官:手机/电脑并不能直接向 RADIUS 服务器发报文。手机把账号密码发给 Wi-Fi AP,AP(作为 NAS)把这些信息打包成 RADIUS 协议格式,再发给 FreeRADIUS。
- 执行者:FreeRADIUS 只是个决策大脑,回复说 “同意上线”。真正的物理开门动作(比如给这个端口通网、分配 VLAN、限制网速)是由 NAS(AP或交换机)来执行的。
1 | [ 手机/电脑 ] ───(1. 传账号密码)───> [ NAS (如 Wi-Fi AP) ] ───(2. 转成 RADIUS 报文)───> [ FreeRADIUS ] |
知道了 NAS 是交换机或 AP,NAS-Port 指的就是用户当前连接的是 NAS 设备的 “哪一个具体物理/逻辑端口”。
- 交换机场景(很直观): 如果你的电脑插在企业交换机的第 5 号网线接口上,那么交换机发给 FreeRADIUS 的报文里就会带有:NAS-Port = 5。
- 无线 Wi-Fi 场景:AP 上没有物理插槽,NAS-Port 常常是一个逻辑编号(比如第几个无线信道/虚拟接口),或者是无线网卡的关联 ID(Association ID)。
- VPN 场景:的是当前的 VPN 虚拟隧道编号(比如第 3 个 VPN 连入会话)。
抓包及分析
在容器抓包
因为 RADIUS 报文是明文 UDP 传输(除了密码字段会加密),我们可以直接在客户端或服务端容器内使用经典的命令行抓包工具 tcpdump 抓取数据,保存为 xxx.pcap 文件,然后拿到宿主机上用 Wireshark 界面打开分析。
第一步:在服务端容器安装 tcpdump。
1 | docker exec -it radius-server /bin/bash |
第二步:在服务端容器内执行抓包命令(监听 RADIUS 默认的 1812 认证端口和 1813 计费端口)。建议:因为我们的 /etc/raddb 目录是挂载到宿主机的 fr-test/config 的,把文件存在容器内的 /etc/raddb/ 下,宿主机对应的 fr-test/config/ 目录下就会实时同步出现这个 radius_test.pcap 文件。
1 | tcpdump -i any udp port 1812 or port 1813 -w /etc/raddb/zdemo/radius_1812_pap_test.pcap |
第三步:保持抓包命令运行,打开另一个终端进入 radius-client 容器发起认证:
1 | docker exec -it radius-client bash |
第四步:回到抓包终端按 Ctrl + C 停止抓包。此时在宿主机的 fr-test/config/zdemo/radius_1812_pap_test.pcap 就是抓到的数据包,你可以直接双击用宿主机上安装的 Wireshark 打开!
Wireshark 实时抓包
如果你想体验 Wireshark 界面实时看到数据包滚动的感觉,可以直接让 Wireshark 去监听 Docker 自动创建的那个虚拟网桥网络(fr-test_default)。也许你在 macOS wireshark 过滤框输入 radius 或者 udp.port==1812 什么也没有抓到。 因为 macOS 宿主机并不是 Linux!Docker Desktop 实际上是在 macOS 后台开了一层隐形的 Linux 微型虚拟机(LinuxKit)。 当你在容器之间(radius-client – radius-server)发包时,数据包完全运行在这个 Linux 虚拟机的虚拟网卡上,根本不会流经 macOS 宿主机的物理网卡(如 en0 或 lo0)!所以你在宿主机直接打开 Wireshark 去抓包,自然什么都抓不到。
方案一:使用管道
把容器流量 “管道串联” 实时推送到 macOS 宿主机的 Wireshark(最优雅,实时看图):你可以利用 docker exec 配合 tcpdump,把容器内部捕获到的原始数据流,通过管道(Pipe)实时喂给 macOS 宿主机的 Wireshark 界面。打开 macOS 终端,直接粘贴并运行这一行命令:
1 | # -w - 表示把抓到的二进制原始数据流不存磁盘,直接输出到控制台标准输出 |
运行命令后,macOS 会自动弹出一个 Wireshark 窗口。你再去 radius-client 容器里触发一次 radtest,宿主机的 Wireshark 界面就会实时滚动出绿色的 RADIUS 报文!在顶部的 Filter 框里写 radius 就能完美匹配过滤。
方案二:修改网卡
如果你不想用管道命令,而是想像平常一样打开 Wireshark 点选网卡直接抓,就需要强制把流量暴露到 macOS 宿主机本地的 Loopback(127.0.0.1)网卡。
修改 docker-compose.yml,把 radius-server 的端口映射绑定到宿主机的 127.0.0.1 上:
1 | ports: |
测试时使用 localhost 或 127.0.0.1
1 | $ radtest owlias 123456 127.0.0.1 0 testing123 |
在 macOS 打开 Wireshark,选择 Loopback: lo0 这个网卡进行抓包。此时因为流量穿过了 macOS 的宿主机网络,在顶部的显示过滤器(Display Filter)输入 radius,就能 100% 抓取到了!

RADIUS PAP 认证分析
上述我们通过 radtest owlias 123456 127.0.0.1 0 testing123 发送的就是一个 radius PAP 认证(Password Authentication Protocol)。在 RADIUS 的 PAP 认证中,明文密码通过 MD5 异或(XOR)算法结合共享密钥(Secret)和随机验证字(Request Authenticator)加密。它的加密机制设计得很巧妙,既隐藏了明文,又通过随机数确保每次抓包看到的密文都不相同(哪怕两次发的是同一个密码)。
计算公式,假设明文密码(P)= 123456、共享密钥(S)= testing123、请求随机数(RA)= Wireshark 中 Access-Request 头部自带的 16 字节随机数 Authenticator
- 第一步:补齐 16 字节块(Padding)。RADIUS 规定密码必须按 16 字节(128 bits)对齐。如果明文密码长度不足 16 字节,右侧必须补 0x00 (NULL);如果超过 16 字节,则切分成多个 16 字节块($P_1, P_2, \dots$)依次迭代计算。明文 123456 的 ASCII 十六进制为:31 32 33 34 35 36(6 字节)。补齐到 16 字节后的 $P_1$:$\text{P}_1 = \text{31 32 33 34 35 36 00 00 00 00 00 00 00 00 00 00}$
- 第二步:生成掩码密钥块(Key Stream):将共享密钥 (S) 和随机数 (RA) 拼接起来,计算一次MD5 哈希,生成一个 16 字节的掩码 $b_1$:$b_1 = \text{MD5}(S + RA)$。由于 $RA$ 是客户端每次发送请求时随机生成的,所以哪怕密码和共享密钥完全不变,每次算出来的 $b_1$ 掩码也绝对不一样!
- 第三步:异或运算(XOR)得到密文。将第一步的 $P_1$ 与第二步的 $b_1$ 进行按位异或($\oplus$),结果 $C_1$ 就是你在 Wireshark 里看到的十六进制密文:$C_1 = P_1 \oplus b_1$。如果密码很长(比如 20 字节),分成了 $P_1$ 和 $P_2$,后续块的计算会利用上一步的密文进行链式迭代(CBC 模式的思想):$b_2 = \text{MD5}(S + C_1)$,$C_2 = P_2 \oplus b_2$,最终将所有密文块拼接:$\text{密文} = C_1 + C_2 + \dots$
1 | import java.nio.charset.StandardCharsets; |
RADIUS PAP 认证全景交互图
认证的全景图
PAP 是 RADIUS 中最古老、最简单,但也最直观的认证协议。它的核心特点是:单向认证(客户端认证服务端,但服务端不认证客户端)、一次握手(One-round trip)、明文传输(通过共享密钥与随机数 MD5 掩码加密密码)。
1 | [ 用户终端 ] [ NAS (网关/AP/交换机) ] [ FreeRADIUS 服务器 ] |
详细步骤拆解
阶段一:终端到 NAS(网关接入)
- 用户提交凭据: 用户在 Portal 页面、VPN 客户端或连 Wi-Fi 时输入 User-Name = “owlias” 和 Password = “123456”。终端通过 HTTP/EAP/802.1X 协议发给 NAS(比如交换机或无线 AP)
阶段二:NAS 封装并发起 RADIUS 请求。NAS 收到用户的账号密码后,并不会直接转发明文,而是需要按照 RADIUS 规范构建一个 Access-Request (Code 1) UDP 报文:
- 生成请求随机数(Request Authenticator, 简称 RA): NAS 随机生成一个 16 字节(128-bit)的随机数 RA,作为本次 RADIUS 报文头的 Authenticator 字段。防止重放攻击(Replay Attack),并作为后续加密密码的盐值(Salt)。
- 加密密码(MD5 XOR):NAS 拿自己与 FreeRADIUS 事先配置好的 共享密钥(Shared Secret,如 testing123)和刚生成的 RA 做运算:
- 掩码 $b_1 = \text{MD5}(\text{Secret} + \text{RA})$
- 密文 $C_1 = P_1 \oplus b_1$(其中 $P_1$ 是右侧补零齐到 16 字节的明文密码)
- 将 $C_1$ 放入属性列表中的 User-Password (Type 2)。
- 发送 Access-Request 报文:NAS 通过 UDP 1812 端口发往 FreeRADIUS,报文中包含:
- Code: 1 (Access-Request)
- Identifier: 随机匹配 ID(如 100,用于匹配后期的 Response)
- Authenticator: 生成的 16 字节 RA
- Attributes:
- User-Name: “owlias”
- User-Password: 加密后的十六进制密文
- NAS-IP-Address: NAS 自身的 IP
- NAS-Port: 用户接入的具体端口号
阶段三:FreeRADIUS 服务端接收与管道处理。报文到达 FreeRADIUS 后,进入我们之前提到的生命周期管道:
- 安全校验与识别(Client Check):
- FreeRADIUS 收到 UDP 报文,先查 clients.conf:发起方的 IP 是否在允许的 NAS 列表内?如果不在,直接丢弃报文(Silent Discard)。如果在,取出对应的 Shared Secret,准备解密。
- authorize 管道与密码还原:
- 解密密码:取报文头的 RA,重新计算 $b_1 = \text{MD5}(\text{Secret} + \text{RA})$,推导出明文密码 $P_1 = C_1 \oplus b_1$。
- 查找用户:FreeRADIUS 拿着 User-Name = “owlias” 去查数据库或 users 文件,找到该用户的预存真实密码或哈希值(例如查到 Cleartext-Password = “123456”)。
- authenticate 管道(密码比对):FreeRADIUS 的 pap 模块将 “解密出的明文密码” 与 “数据库里查到的密码” 做字符串比对(或 Hash 比对)。如果一致,判定认证成功,返回 ok。如果不一致,判定认证失败,准备构建 Access-Reject。
阶段四:FreeRADIUS 响应与决策防篡改。
- 生成响应验证字(Response Authenticator): 为了防止黑客在网络中伪造响应包(比如黑客截获后伪造一个 Access-Accept 骗过交换机),FreeRADIUS 必须对响应报文进行签名:$\text{Response Authenticator} = \text{MD5}(\text{Code} + \text{ID} + \text{Length} + \text{RA} + \text{Attributes} + \text{Secret})$
- 发送响应报文:
- 认证成功:发送 Access-Accept (Code 2),可以附带授权属性(如 Reply-Message,分配给用户的 VLAN ID Tunnel-Private-Group-Id,网速限制属性等)。
- 认证失败:发送 Access-Reject (Code 3),拒绝用户上线。
阶段五:NAS 执行接入控制
- NAS 收到响应后,用同样的算法校验 Response Authenticator 的签名是否合法。
- 如果合法且是 Access-Accept,NAS 放行端口,把用户加入对应 VLAN,用户成功通网!
PAP 认证的优缺点
- 优点:极其简单高效。无复杂多次 TLS 握手,一个 Request、一个 Response 搞定,数据库校验性能极高。
- 致命缺点:弱安全性(密码易被破解)。MD5 算法已经被证实不安全。只要在网络中抓到包,且知道共享密钥(或者对 Shared Secret 进行字典爆破),就能直接通过异或运算还原出明文密码。
- 使用的建议:绝不建议直接在公共网络中使用。仅用于完全隔离的安全内网/专线,或者作为外层已建立 TLS 隧道(如 PEAP-GTC / TTLS-PAP)内部的二次认证协议。
PAP 方案的改进 CHAP
Portal+AC+Radius 案例
既然 PAP 发送明文密码(仅靠弱 MD5 加密)不安全,人们自然想到了用挑战/响应(Challenge-Response)机制的协议——比如 CHAP(Challenge Handshake Authentication Protocol)。
CHAP 最大的革新在于:在整个网络认证过程中,用户的密码永远不需要在网络线上传输!我们以一个更贴近实际组网(校园网/企业网常见的 Portal+AC+RADIUS 架构)的场景,报文比单纯的 PPP CHAP 多了一层——Portal Server 与 AC 之间还有一套 Portal 协议(国内厂商如华为/H3C 常用,UDP 2000端口,并非 IETF 标准,但字段含义业界基本通用)。


① 客户端访问外网 → 被重定向
用户浏览器发起 HTTP 请求,AC (或与之联动的接入设备)检测到该终端 MAC/IP 未认证,回一个 302,把浏览器导向 Portal Server 的登录页 URL (URL 里通常带上原始访问地址、AC 地址、NAS-ID 等参数,便于后续关联)。
② Portal Server → AC:REQ_CHALLENGE (Portal 协议,通常 UDP 2000)
关键字段: Type=1(REQ_CHALLENGE)、SerialNo (序列号用于后续请求/响应配对)、ReqID、UserIP (待认证终端 IP)、AuthType=CHAP。
③ AC → Portal Server:ACK_CHALLENGE
关键字段: Type=2、SerialNo (原样带回)、ErrCode=0、Challenge (AC 生成的 16 字节随机挑战码——这是整条链路里唯一的 “随机源”)。
④ Portal Server 下发登录页给浏览器
把 Challenge 嵌入登录页(HTML/JS)一并推给浏览器,此时 Challenge 还没做任何运算。
⑤ 浏览器提交表单 → Portal Server
浏览器本地执行: CHAP-Password = MD5( (byte) ReqID + 明文密码 + Challenge ) 其中“+”表示字节数组的拼接。提交的字段是计算之后的MD5摘要。这是全流程中密码明文唯一 “存在” 的地方——仅在浏览器本地内存中参与运算,从未上过网线。
⑥ Portal Server → AC:REQ_AUTH
关键字段: Type=3、SerialNo/ReqID (关联步骤②③的会话)、UserName、Password 字段 (装的是⑤算出的 CHAP-Password 摘要,不是明文)、Challenge(原样带回)、AuthType=CHAP。
⑦ AC → FreeRADIUS:Access-Request (RADIUS 协议,UDP 1812)。AC 把 Portal 报文字段映射成标准 RADIUS 属性:
- User-Name(1)= 用户名
- CHAP-Password(3)= 1 字节 CHAP Identifier + 16 字节摘要,这里的 CHAP Identifier=(byte)(ReqID)
- CHAP-Challenge(60)= AC 生成的那个随机挑战码
- NAS-IP-Address(4)、NAS-Port 等设备信息
⑧ 服务器端比对(FreeRADIUS 内部)
FreeRADIUS 从用户数据库 users 文件中取出该账号的 Cleartext-Password,用同样公式重新计算 MD5(CHAP Ident + 明文密码 + CHAP-Challenge)与请求包里 CHAP-Password 属性中的摘要部分逐字节比对。这一步要求服务器必须能拿到密码明文或可逆形式——这也是 CHAP 相对存储更简单的哈希方案(如只存 SHA 摘要)的代价。
⑨ FreeRADIUS → AC:Access-Accept / Access-Reject
- Accept 关键字段: Service-Type、Session-Timeout、Class(计费关联用)、可选 Framed-IP-Address
- Reject 关键字段: Reply-Message (失败原因文本)
⑩ AC → Portal Server:ACK_AUTH
关键字段: Type=4、SerialNo/ReqID、ErrCode(0=成功)、在线时长/流量限制等策略字段。最后,Portal Server 给浏览器返回认证成功/失败页面,AC 侧同步放行该 IP/MAC 的上网权限。
需要说明的是,Portal 协议本身不是 IETF 标准,字段名称(Type/SerialNo/ReqID 等)是华为、H3C 等厂商私有实现里的常见叫法,不同厂商在细节上会有差异,但 “AC 下发 Challenge → 浏览器本地做 MD5 摘要 → 逐跳透传摘要而非密码” 这个核心链路是一致的。RADIUS 侧的 CHAP-Password(属性3)和 CHAP-Challenge(属性60)则是 RFC 2865/2869 里的标准属性。
RADIUS CHAP 抓包
我们继续使用 wireshark 进行抓包测试,在测试端发送一个chap模拟请求:
1 | $ radtest -t chap owlias 123456 radius-server 0 testing123 |
抓包内容:

CHAP 认证算法实现
1 | import java.nio.charset.StandardCharsets; |
CHAP 安全吗
那么,CHAP 能在公网上安全传输吗?它足够安全吗?答案是:CHAP 比 PAP 安全得多(密码不再在网络上传输),但直接裸奔在公网上,它依然不够安全!现代公网传输真正的终极替代方案是 TLS 隧道(如 EAP-TTLS / PEAP)。之所以说 CHAP 还不够安全,是因为:
- 为了能够计算摘要进行比对,CHAP 必须在数据库存储明文密码(或者能够反推出明文的密码)。
- 攻击者只要能抓取一个包,就能够获取到摘要、ReqID、Challenge,利用这些信息他就可以在本地进行每秒几十亿次的爆破,对于现代密码学,MD5 已经不算安全了。